package org.chen.ctrip.service.impl;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.chen.common.ctrip.crawler.CrawlRequest;
import org.chen.common.ctrip.crawler.CrawlResult;
import org.chen.common.ctrip.crawler.CrawlerExecutionResult;
import org.chen.common.ctrip.dto.AiProductResponse;
import org.chen.common.ctrip.dto.ProductCreateContentRequest;
import org.chen.common.ctrip.dto.ProductRequest;
import org.chen.common.ctrip.entity.Product;
import org.chen.common.ctrip.entity.ProductMultilang;
import org.chen.common.system.province.ProvinceCapital;
import org.chen.common.result.ResultVO;
import org.chen.ctrip.feign.SystemClient;
import org.chen.ctrip.mapper.ProductMapper;
import org.chen.ctrip.mapper.ProductMultilangMapper;
import org.chen.ctrip.service.ContentReplaceRuleService;
import org.chen.ctrip.service.CustomDayContentService;
import org.chen.ctrip.service.ProductMultilangService;
import org.chen.ctrip.service.ProductService;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.task.TaskExecutor;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

@Slf4j
@Service
public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> implements ProductService {

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private ContentReplaceRuleService contentReplaceRuleService;

    @Autowired
    private QdrantHttpService qdrantHttpService;

    @Autowired
    private CustomDayContentService customDayContentService;

    @Autowired
    private SystemClient systemClient;

    @Resource
    private TaskExecutor taskExecutor;

    @Autowired
    private HtmlContentProcessor htmlContentProcessor;

    @Autowired
    private ProductMultilangService productMultilangService;

    private static final String COOKIE = "UBT_VID=1743501440388.a3d4ozuhoWXu; GUID=09031085319942862668; MKT_CKID=1743501440760.o05yh.igf5; _RSG=ZKKDZQ0euC5DTjFQJyI1H9; _RDG=283c7815d357f02f06043dab73c3d9196a; _RGUID=5338a006-a335-4523-9441-4bc4da3ed74d; _abtest_userid=2cd76af2-7952-468b-b3b8-385f2987f2ac; nfes_isSupportWebP=1; _bfaStatusPVSend=1; ibulocale=zh_cn; cookiePricesDisplayed=CNY; _ga_P282SR8WVH=GS2.1.s1750674413$o2$g0$t1750674413$j60$l0$h0; voticket=6D81A1E6D51A957116DD8409ABADAF77D8C21EC7EC0FB79CBB7F56193075DB41; voduid=u=A7C69B47C5B92FD8BE7A537341EEF986FC6B7346868BC43A98766B342D31186A&v=0; ttdstp-locale-lang=zh-CN; vbk-locale-lang=zh-CN; intl_ht1=h4=359_1734913,28_115646082,28_102868730,28_54424274,28_106557238,359_996749; ibulanguage=CN; _ga=GA1.1.1817492855.1743501483; MKT_Pagesource=PC; _ga_9BZF483VNQ=GS2.1.s1753326373$o14$g1$t1753326395$j38$l0$h0; _ga_5DVRDQD429=GS2.1.s1753326373$o15$g1$t1753326395$j38$l0$h0; _ga_B77BES1Z8Z=GS2.1.s1753326373$o15$g1$t1753326395$j38$l0$h0; StartCity_Pkg=PkgStartCity=28; Hm_lvt_a8d6737197d542432f4ff4abc6e06384=1750922206,1752137437,1753326381,1753344667; Union=OUID=&AllianceID=4902&SID=22921635&SourceID=&createtime=1753344667&Expires=1753949467096; MKT_OrderClick=ASID=490222921635&AID=4902&CSID=22921635&OUID=&CT=1753344667097&CURL=https%3A%2F%2Fwww.ctrip.com%2F%3Fallianceid%3D4902%26sid%3D22921635%26msclkid%3D1c81423d8fe81af194302abc657d664a%26keywordid%3D82327063055662&VAL={\"pc_vid\":\"1743501440388.a3d4ozuhoWXu\"}; _ubtstatus=%7B%22vid%22%3A%221743501440388.a3d4ozuhoWXu%22%2C%22sid%22%3A52%2C%22pvid%22%3A7%2C%22pid%22%3A290570%7D; _bfaStatus=success; _RF1=240e%3A39e%3A4f9%3A8b30%3A49b0%3A49f3%3Aaabe%3A5ffb; Session=SmartLinkCode=live&SmartLinkKeyWord=&SmartLinkQuary=_UTF.&SmartLinkHost=ntp.msn.cn&SmartLinkLanguage=zh; FlightIntl=Search=[%22CTU|%E6%88%90%E9%83%BD(CTU)|28|CTU|480%22%2C%22BKK|%E6%9B%BC%E8%B0%B7(BKK)|359|BKK|420%22%2C%222025-07-26%22%2C%222025-07-29%22]; cticket=67F2A8DF900B18FCCE2A6F9B5F4E42206335D30B9F18E21754BFBE195158BDE8; login_type=0; login_uid=786F4A95FBF5968866BD1C617F88879C; DUID=u=786F4A95FBF5968866BD1C617F88879C&v=0; IsNonUser=F; AHeadUserInfo=VipGrade=5&VipGradeName=%B0%D7%D2%F8%B9%F3%B1%F6&UserName=&NoReadMessageCount=1; _udl=708D70C2B179E2F91CC5ED1C2CCE362D; _jzqco=%7C%7C%7C%7C1751368106595%7C1.1032251866.1743501440762.1753409444370.1753409473421.1753409444370.1753409473421.0.0.0.197.197; _bfa=1.1743501440388.a3d4ozuhoWXu.1.1753409473098.1753409480090.54.6.104317";
    
    @Value("${static.page.path}")
    private String staticPagePath;

    private static final String SEARCH_URL = "https://online.ctrip.com/restapi/soa2/20684/productSearch?_fxpcqlniredt=09031085319942862668";
    private static final String DETAIL_URL = "https://vacations.ctrip.com/travel/itinerary";

    private static final String[] USER_AGENTS = {
            "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",
            "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",
            "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:89.0) Gecko/20100101 Firefox/89.0",
            "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
    };

    @Override
    public List<Product> crawlProducts(CrawlRequest crawlRequest) {
        try {
            // 构建请求参数
            Map<String, Object> requestBody = buildRequestBody(
                    crawlRequest.getKeyword(),
                    crawlRequest.getPageIndex(),
                    crawlRequest.getPageSize(),
                    crawlRequest.getDays(),
                    crawlRequest.getDepartureCity(),
                    crawlRequest.getFlag());

            // 设置请求头
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_JSON);
            headers.set("Cookie", COOKIE);
            headers.set("User-Agent", USER_AGENTS[new Random().nextInt(USER_AGENTS.length)]);

            HttpEntity<Map<String, Object>> entity = new HttpEntity<>(requestBody, headers);

            // 发送请求
            ResponseEntity<String> response = restTemplate.postForEntity(SEARCH_URL, entity, String.class);

            if (response.getStatusCode() == HttpStatus.OK) {
                List<Product> products = parseAndSaveProducts(response.getBody());
                if (!products.isEmpty()) {
                    asyncFetchProductDetails(products.stream().map(Product::getId).collect(Collectors.toList()));
                }

                return products;
            }

            throw new RuntimeException("没有商品数据，请尝试更换游玩天数");
        } catch (Exception e) {
            log.error("爬取商品数据失败: {}", e.getMessage(), e);
            throw new RuntimeException(e.getMessage());
        }
    }

    public void asyncFetchProductDetails(List<String> productIds) {
        log.info("开始异步获取{}个商品详情页", productIds.size());

        // 使用线程池异步执行
        CompletableFuture.runAsync(() -> {
            try {
                batchFetchProductDetails(productIds);
            } catch (Exception e) {
                log.error("批量获取商品详情失败", e);
            }
        }, taskExecutor);
    }

    public void batchFetchProductDetails(List<String> productIds) {
        int batchSize = 5; // 每批处理5个
        int delay = 1000; // 批次间延迟1秒

        for (int i = 0; i < productIds.size(); i += batchSize) {
            int endIndex = Math.min(i + batchSize, productIds.size());
            List<String> batch = productIds.subList(i, endIndex);

            log.info("处理第{}批商品详情，数量: {}", (i / batchSize + 1), batch.size());

            // 并发处理当前批次
            List<CompletableFuture<Void>> futures = batch.stream()
                    .map(productId -> CompletableFuture.runAsync(() -> {
                        try {
                            fetchProductDetail(productId);
                            log.debug("成功获取商品{}详情", productId);
                        } catch (Exception e) {
                            log.error("获取商品{}详情失败: {}", productId, e.getMessage());
                        }
                    }, taskExecutor))
                    .toList();

            // 等待当前批次完成
            CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();

            // 批次间延迟，避免请求过于频繁
            if (endIndex < productIds.size()) {
                try {
                    Thread.sleep(delay);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    log.warn("延迟被中断");
                    break;
                }
            }
        }

        log.info("批量获取商品详情完成");
    }

    @Override
    public List<Product> getUnfetchedDetailProducts() {
        QueryWrapper<Product> wrapper = new QueryWrapper<>();
        wrapper.eq("detail_fetched", false);
        wrapper.ne("product_type", "DT"); // 过滤掉DT类型
        return list(wrapper);
    }

    @Override
    public Product fetchProductDetail(String productId) {
        try {
            // 获取商品信息
            Product product = getById(productId);
            if (product == null) {
                log.warn("商品不存在: {}", productId);
                throw new RuntimeException("商品不存在:" + productId);
            }
            
            // 出发城市
            JSONArray departures = JSON.parseArray(product.getDepartures());
            if (departures.isEmpty()) {
                log.warn("商品{}没有出发城市信息", productId);
                throw new RuntimeException("商品" + productId+"没有出发城市信息");
            }

            String departCityId = departures.getJSONObject(0).getString("id");
            String departCityName = departures.getJSONObject(0).getString("name");
            product.setCity(departCityName);

            // 到达城市
            JSONArray locations = JSON.parseArray(product.getLocations());
            if (locations.isEmpty()) {
                log.warn("商品{}没有到达城市信息", productId);
                throw new RuntimeException("商品"+ productId +"没有到达城市信息");
            }

            Long locationsCityId = locations.getJSONObject(0).getLong("id");
            String locationsCityName = locations.getJSONObject(0).getString("name");
            product.setLocationsCity(locationsCityName);

            String detailUrl;
            if (locationsCityId != null && !locationsCityId.equals(0L)) {

                // 构建详情页URL
                detailUrl = String.format("%s?productId=%s&departCityId=%s&lineid=0&visaid=0",
                        DETAIL_URL, productId, locationsCityId);
            } else {
                // 构建详情页URL
                detailUrl = String.format("%s?productId=%s&departCityId=%s&lineid=0&visaid=0",
                        DETAIL_URL, productId, 28); // 默认成都
            }

            // 设置请求头
            HttpHeaders headers = new HttpHeaders();
            headers.set("Cookie", COOKIE);
            headers.set("User-Agent", USER_AGENTS[new Random().nextInt(USER_AGENTS.length)]);
            
            HttpEntity<String> entity = new HttpEntity<>(headers);
            
            // 获取HTML内容
            ResponseEntity<String> response = restTemplate.exchange(detailUrl, HttpMethod.GET, entity, String.class);
            
            if (response.getStatusCode() == HttpStatus.OK) {
                String htmlContent = response.getBody();

                // 先进行基础HTML处理
                String processedHtml = processHtml(htmlContent);

                // 应用内容替换规则
                String ruleProcessedHtml  = contentReplaceRuleService.applyReplaceRules(processedHtml);

                // 提取页面内容（行程天数标题）
                String pageContent = extractPageContent(ruleProcessedHtml);
                String pageDiv = extractPageDiv(ruleProcessedHtml);

//                String fileName = productId + ".html";
//                String filePath = saveStaticPage(ruleProcessedHtml, fileName);
                
                // 更新数据库
                product.setDetailFetched(true);
//                product.setLocalPagePath(filePath);
                product.setPageContent(pageContent);
                product.setPageDiv(pageDiv);
//                product.setStaticPagePath( "http://192.168.0.13:9001/data/" + fileName);
                product.setUpdateTime(LocalDateTime.now());
                updateById(product);
                
                log.info("成功获取商品{}详情页", productId);

                // 保存到向量数据库
                try {
                    Object o = qdrantHttpService.saveProductVector(product, departCityId);
                    log.info("成功保存商品{}的向量数据", productId);
                } catch (Exception e) {
                    log.error("保存商品{}向量数据失败", productId, e);
                    // 向量保存失败不影响主流程
                }
            }
            return product;

        } catch (Exception e) {
            log.error("获取商品{}详情失败: {}", productId, e.getMessage(), e);
            throw new RuntimeException(e.getMessage());
        }
    }

    @Override
    public CrawlerExecutionResult crawlProductsRange(String keyword, int startPage, int endPage, int pageSize, int days, String departureCity) {
        CrawlerExecutionResult taskResult = new CrawlerExecutionResult();
        taskResult.setTaskName("页数范围爬取");
        taskResult.setKeywords(keyword);
        taskResult.setStartPageIndex(startPage);
        taskResult.setEndPageIndex(endPage);
        taskResult.setPageSize(pageSize);
        taskResult.setExecuteTime(LocalDateTime.now());
        taskResult.setDepartureCity(departureCity);

        long taskStartTime = System.currentTimeMillis();
        StringBuilder executionDetail = new StringBuilder();
        boolean hasError = false;

        try {
            log.info("开始页数范围爬取: 关键词={}, 页码范围={}-{}, 每页数量={}",
                    keyword, startPage, endPage, pageSize);

            // 爬取指定范围的页数
            for (int currentPage = startPage; currentPage <= endPage; currentPage++) {
                CrawlerExecutionResult.PageResult pageResult = new CrawlerExecutionResult.PageResult(currentPage);
                long pageStartTime = System.currentTimeMillis();

                try {
                    log.debug("开始爬取第{}页", currentPage);

                    // 爬取单页数据
                    CrawlResult crawlResult = crawlProductsWithResult(keyword, currentPage, pageSize, days, departureCity);

                    pageResult.setSuccess(crawlResult.isSuccess());
                    pageResult.setProductCount(crawlResult.getTotalCount());
                    pageResult.setNewProductCount(crawlResult.getNewCount());
                    pageResult.setUpdateProductCount(crawlResult.getUpdateCount());
                    pageResult.setDuration(System.currentTimeMillis() - pageStartTime);

                    if (!crawlResult.isSuccess()) {
                        pageResult.setErrorMessage(crawlResult.getMessage());
                        hasError = true;
                        log.warn("第{}页爬取失败: {}", currentPage, crawlResult.getMessage());
                    } else {
                        log.debug("第{}页爬取成功: 总数={}, 新增={}, 更新={}",
                                currentPage, crawlResult.getTotalCount(), crawlResult.getNewCount(), crawlResult.getUpdateCount());
                    }

                    executionDetail.append(String.format("第%d页: %s(总数%d, 新增%d, 更新%d); ",
                            currentPage, crawlResult.isSuccess() ? "成功" : "失败",
                            crawlResult.getTotalCount(), crawlResult.getNewCount(), crawlResult.getUpdateCount()));

                } catch (Exception e) {
                    pageResult.setSuccess(false);
                    pageResult.setErrorMessage(e.getMessage());
                    pageResult.setDuration(System.currentTimeMillis() - pageStartTime);
                    hasError = true;

                    log.error("第{}页爬取异常", currentPage, e);
                    executionDetail.append(String.format("第%d页: 异常(%s); ", currentPage, e.getMessage()));
                }

                // 添加页面结果到任务结果中
                taskResult.addPageResult(pageResult);

                // 页面之间添加延时，避免请求过于频繁
                if (currentPage < endPage) {
                    try {
                        Thread.sleep(2000); // 2秒延时
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                        log.warn("延时被中断");
                    }
                }
            }

            taskResult.setSuccess(!hasError);
            taskResult.setExecutionDetail(executionDetail.toString());

            log.info("页数范围爬取完成: 关键词={}, 总页数={}, 成功页数={}, 失败页数={}, 总商品数={}, 新增={}, 更新={}",
                    keyword, taskResult.getTotalPages(), taskResult.getSuccessPages(), taskResult.getFailedPages(),
                    taskResult.getProductCount(), taskResult.getNewProductCount(), taskResult.getUpdateProductCount());

        } catch (Exception e) {
            taskResult.setSuccess(false);
            taskResult.setErrorMessage("任务执行异常: " + e.getMessage());
            taskResult.setExecutionDetail(executionDetail.append("任务异常: ").append(e.getMessage()).toString());
            log.error("页数范围爬取任务异常: 关键词={}", keyword, e);
        } finally {
            taskResult.setTotalDuration(System.currentTimeMillis() - taskStartTime);
        }

        return taskResult;
    }

    @Override
    public CrawlResult crawlProductsWithResult(String keyword, int pageIndex, int pageSize, int days, String departureCity) {
        CrawlResult result = new CrawlResult();

        try {
            log.info("开始爬取商品数据: 关键词={}, 页码={}, 每页数量={}", keyword, pageIndex, pageSize);

            // 构建请求参数
            Map<String, Object> requestBody = buildRequestBody(keyword, pageIndex, pageSize, days, departureCity, true);

            // 设置请求头
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_JSON);
            headers.set("Cookie", COOKIE);
            headers.set("User-Agent", USER_AGENTS[new Random().nextInt(USER_AGENTS.length)]);

            HttpEntity<Map<String, Object>> entity = new HttpEntity<>(requestBody, headers);

            // 发送请求
            ResponseEntity<String> response = restTemplate.postForEntity(SEARCH_URL, entity, String.class);

            if (response.getStatusCode() == HttpStatus.OK) {
                // 解析并保存商品，返回统计结果
                result = parseAndSaveProductsWithResult(response.getBody());

                log.info("爬取商品数据完成: 关键词={}, 总数={}, 新增={}, 更新={}",
                        keyword, result.getTotalCount(), result.getNewCount(), result.getUpdateCount());
            } else {
                result.setSuccess(false);
                result.setMessage("HTTP请求失败，状态码: " + response.getStatusCode());
                log.error("爬取商品数据失败: {}", result.getMessage());
            }

        } catch (Exception e) {
            result.setSuccess(false);
            result.setMessage("爬取异常: " + e.getMessage());
            log.error("爬取商品数据异常: 关键词={}", keyword, e);
        }

        return result;
    }

    @Override
    public Product updateProduct(ProductRequest productRequest) {
        Product byId = getById(productRequest.getId());
        if (byId == null) {
            throw new RuntimeException("商品不存在");
        }

        String pageContent = extractPageContent(productRequest.getPageDiv());
        byId.setPageContent(pageContent);
        byId.setTitle(productRequest.getTitle());
        byId.setPageDiv(productRequest.getPageDiv());

        boolean b = updateById(byId);
        if (b) {
            JSONArray departures = JSON.parseArray(byId.getDepartures());
            if (departures.isEmpty()) {
                log.warn("商品{}没有出发城市信息", productRequest.getId());
                throw new RuntimeException("商品" + productRequest.getId()+"没有出发城市信息");
            }

            String departuresCityId = departures.getJSONObject(0).getString("id");

            log.info("更新商品{}的向量数据，出发城市ID: {}", productRequest.getId(), departuresCityId);

            Object o = qdrantHttpService.saveProductVector(byId, departuresCityId);
        } else {
            log.error("更新商品{}失败", productRequest.getId());
            throw new RuntimeException("更新商品失败");
        }

        return byId;
    }

    @Override
    @Transactional
    public boolean deleteContentById(String id) {
        Product byId = getById(id);
        if (byId == null) {
            throw new RuntimeException("商品不存在");
        }

        boolean deleted = removeById(id);
        if (deleted) {
            log.info("成功删除商品: {}", id);

            productMultilangService.removeById(id);

            String departuresCityId;

            if (byId.getIsAi()) {

                departuresCityId = byId.getCity();

            } else {
                JSONArray departures = JSON.parseArray(byId.getDepartures());
                if (departures.isEmpty()) {
                    log.warn("商品{}没有出发城市信息", byId.getId());
                    throw new RuntimeException("商品" + byId.getId()+"没有出发城市信息");
                }

                departuresCityId = departures.getJSONObject(0).getString("id");
            }

            // 删除向量数据
            qdrantHttpService.deleteProductVector(id, departuresCityId);

            // 删除静态页面文件
            String staticPagePath = byId.getLocalPagePath();
            if (staticPagePath != null && !staticPagePath.isEmpty()) {
                File file = new File(staticPagePath);
                if (file.exists() && file.isFile()) {
                    boolean deletedFile = file.delete();
                    if (!deletedFile) {
                        log.warn("删除静态页面文件失败: {}", staticPagePath);
                    } else {
                        log.info("成功删除静态页面文件: {}", staticPagePath);
                    }
                } else {
                    log.warn("静态页面文件不存在或不是文件: {}", staticPagePath);
                }
            }

            log.info("成功删除商品{}的向量数据", id);
            return true;
        } else {
            log.error("删除商品{}失败", id);
            return false;
        }
    }

    @Override
    public AiProductResponse createContent(ProductCreateContentRequest productCreateContentRequest) {
        long id = IdWorker.getId();

        AiProductResponse aiProductResponse = new AiProductResponse();

        Product product = new Product();
        String productId = "Ai-" + id;
        product.setId(productId);
        product.setTitle(productCreateContentRequest.getTitle());
        product.setPageDiv(productCreateContentRequest.getPageDiv());
        product.setPageContent(productCreateContentRequest.getContent());
        product.setCity(productCreateContentRequest.getCity());
        product.setDurationDays(productCreateContentRequest.getDurationDays());
        product.setTags(productCreateContentRequest.getTags());
        product.setDetailFetched(true);
        product.setIsAi(true);
        product.setAskAi(true);
        product.setCreateTime(LocalDateTime.now());
        product.setUpdateTime(LocalDateTime.now());
        product.setProductType("AI"); // 标记为AI生成内容

        boolean save = save(product);
        if (save) {
            log.info("成功创建商品: {}", productId);

            // 保存向量数据
            Object o = qdrantHttpService.saveProductVector(product, product.getCity());
            BeanUtils.copyProperties(product, aiProductResponse);
            aiProductResponse.setProductId(o);

            return aiProductResponse;
        } else {
            log.error("创建商品失败");
            throw new RuntimeException("创建商品失败");
        }
    }

    @Override
    public Product getByIdAndLanguage(String id, String lang) {
        log.info("查询参数 --->> 商品id：{}, 语言：{}", id, lang);
        if (lang == null || lang.isEmpty() || "zh".equalsIgnoreCase(lang) || "cn".equalsIgnoreCase(lang) || "zh-cn".equalsIgnoreCase(lang)) {
            // 如果不指定语言或指定为中文，直接返回中文内容
            return getById(id);
        } else {
            Product byId = getById(id);

            if (byId == null) {
                throw new RuntimeException("商品不存在");
            }

            ProductMultilang one = productMultilangService.getOne(new QueryWrapper<ProductMultilang>().eq("product_id", id).eq("language_code", lang));
            if (one != null) {
                byId.setPageDiv(one.getTranslatedContent());
                byId.setLanguage(lang);
                byId.setTitle(one.getTitle());
                // 如果找到对应的语言翻译，返回翻译后的内容
                return byId;
            }

            // 如果没有找到对应语言的翻译，返回默认中文内容
            return byId;
        }
    }

    @Override
    public void syncData() {
        List<Product> allProducts = list();
        if (allProducts == null || allProducts.isEmpty()) {
            log.info("没有需要同步的商品数据。");
            return;
        }

        int batchSize = 100; // 定义每个批次的大小
        int totalProducts = allProducts.size();

        for (int i = 0; i < totalProducts; i += batchSize) {
            // 计算当前批次的结束索引
            int end = Math.min(i + batchSize, totalProducts);
            // 获取当前批次的子列表
            List<Product> batch = allProducts.subList(i, end);

            log.info("开始处理批次：索引 {} 到 {}，共 {} 个商品。", i, end - 1, batch.size());

            // 遍历当前批次的每一个商品
            batch.forEach(product -> {
                try {
                    String departuresCityId;

                    if (product.getIsAi()) {
                        departuresCityId = product.getCity();
                    } else {
                        JSONArray departures = JSON.parseArray(product.getDepartures());
                        if (departures == null || departures.isEmpty()) {
                            log.warn("商品 {} 没有出发城市信息，已跳过。", product.getId());
                            return; // 使用 return 跳出当前 lambda 表达式，继续下一个
                        }
                        departuresCityId = departures.getJSONObject(0).getString("id");
                    }

                    // 调用单条保存的方法
                    qdrantHttpService.saveProductVector(product, departuresCityId);

                } catch (Exception e) {
                    // 捕获单个商品处理时可能发生的任何异常，记录日志后继续处理下一个
                    log.error("处理商品 {} 时发生异常，已跳过。错误信息: {}", product.getId(), e.getMessage(), e);
                }
            });

            log.info("批次处理完成：索引 {} 到 {}。", i, end - 1);
        }
        log.info("所有商品数据同步完成，总数：{}", totalProducts);
    }

    /**
     * 解析商品数据并保存，返回统计结果
     */
    private CrawlResult parseAndSaveProductsWithResult(String responseBody) {
        CrawlResult result = new CrawlResult();
        int totalProcessed = 0;
        int newProducts = 0;
        int updatedProducts = 0;

        try {
            JSONObject jsonResponse = JSON.parseObject(responseBody);
            JSONArray products = jsonResponse.getJSONArray("products");

            if (products == null || products.isEmpty()) {
                log.warn("响应中没有找到商品数据");
                result.setMessage("未找到商品数据");
                return result;
            }

            for (int i = 0; i < products.size(); i++) {
                JSONObject productJson = products.getJSONObject(i);

                // 过滤掉DT类型
                String productType = productJson.getString("productType");
                if ("DT".equals(productType)) {
                    continue;
                }

                String productId = productJson.getString("id");
                if (productId == null || productId.isEmpty()) {
                    continue;
                }

                totalProcessed++;

                // 检查是否已存在
                Product existingProduct = getById(productId);
                boolean isNew = (existingProduct == null);

                Product product = isNew ? new Product() : existingProduct;

                // 填充商品数据
                populateProductData(product, productJson);

                // 设置时间戳
                if (isNew) {
                    product.setDetailFetched(false);
                    product.setCreateTime(LocalDateTime.now());
                    newProducts++;
                } else {
                    updatedProducts++;
                }
                product.setUpdateTime(LocalDateTime.now());

                // 保存或更新
                saveOrUpdate(product);

                log.debug("{}商品: ID={}, 标题={}", isNew ? "新增" : "更新", productId, product.getTitle());
            }

            result.setTotalCount(totalProcessed);
            result.setNewCount(newProducts);
            result.setUpdateCount(updatedProducts);
            result.setMessage(String.format("处理完成: 总数%d, 新增%d, 更新%d", totalProcessed, newProducts, updatedProducts));

            log.info("商品数据解析完成: {}", result.getMessage());

        } catch (Exception e) {
            result.setSuccess(false);
            result.setMessage("解析商品数据失败: " + e.getMessage());
            log.error("解析商品数据失败", e);
        }

        return result;
    }

    /**
     * 填充商品数据
     */
    private void populateProductData(Product product, JSONObject productJson) {
        try {
            String productId = productJson.getString("id");
            product.setId(productId);
            product.setProductType(productJson.getString("productType"));

            // 基本信息
            JSONObject basicInfo = productJson.getJSONObject("basicInfo");
            if (basicInfo != null) {
                product.setTitle(contentReplaceRuleService.applyReplaceRules(basicInfo.getString("mainName")));
                product.setContent(contentReplaceRuleService.applyReplaceRules(basicInfo.getString("name")));
                product.setLevel(basicInfo.getInteger("level"));
                product.setMinDays(basicInfo.getInteger("minDays"));
                product.setMaxDays(basicInfo.getInteger("maxDays"));
                product.setDurationDays(basicInfo.getInteger("minDays"));

                // 保存departures和locations为JSON字符串
                JSONArray departures = basicInfo.getJSONArray("departures");
                JSONArray locations = basicInfo.getJSONArray("locations");

                if (departures != null) {
                    product.setDepartures(departures.toJSONString());
                }

                if (locations != null) {
                    product.setLocations(locations.toJSONString());
                }

                // 图片信息
                JSONObject mediaInfo = basicInfo.getJSONObject("mediaInfo");
                if (mediaInfo != null) {
                    JSONArray images = mediaInfo.getJSONArray("images");
                    if (images != null && !images.isEmpty()) {
                        JSONObject firstImage = images.getJSONObject(0);
                        if (firstImage != null) {
                            product.setImageUrl(firstImage.getString("url"));
                        }
                    }
                }

                // 线路标签
                JSONObject extras = basicInfo.getJSONObject("extras");
                String routeSubtitleName = extras.getString("ROUTE_SUBTITLE_NAME");
                String routeLevel1Name = extras.getString("ROUTE_LEVEL1_NAME");
                Set<String> tagSet = extractAndDeduplicateTags(routeSubtitleName, routeLevel1Name);
                product.setTags(tagSet.stream().toList());
            }

            // 价格信息
            JSONObject priceInfo = productJson.getJSONObject("priceInfo");
            if (priceInfo != null) {
                Double price = priceInfo.getDouble("price");
                Double originalPrice = priceInfo.getDouble("originalPrice");

                if (price != null) {
                    product.setPrice(price);
                }
                if (originalPrice != null) {
                    product.setOriginalPrice(originalPrice);
                }
            }

        } catch (Exception e) {
            log.error("填充商品数据失败: productId={}", productJson.getString("id"), e);
            throw new RuntimeException("填充商品数据失败", e);
        }
    }

    /**
     * 提取页面内容 - 解析每天的行程标题
     * @param htmlContent HTML内容
     * @return 格式化的页面内容，如："Day 1: 熊猫基地、宽窄巷子；Day 2: 锦里、杜甫草堂"
     */
    private String extractPageContent(String htmlContent) {
        try {
            Document doc = Jsoup.parse(htmlContent);
            StringBuilder pageContentBuilder = new StringBuilder();

            // 查找行程表格
            Elements tables = doc.select("table.print_table");

            for (Element table : tables) {
                Elements rows = table.select("tbody > tr");

                for (Element row : rows) {
                    Elements cells = row.select("th, td");

                    if (cells.size() >= 2) {
                        Element firstCell = cells.get(0);
                        Element secondCell = cells.get(1);

                        // 检查第一个单元格是否包含Day信息
                        String firstCellText = firstCell.text().trim();
                        if (firstCellText.matches("Day\\s*\\d+")) {
                            // 提取Day数字
                            String dayNumber = firstCellText.replaceAll("\\D+", "");

                            // 获取第二个单元格的行程内容
                            String itineraryContent = extractItineraryContent(secondCell);

                            if (!itineraryContent.isEmpty()) {
                                if (!pageContentBuilder.isEmpty()) {
                                    pageContentBuilder.append("；");
                                }
                                pageContentBuilder.append("Day ").append(dayNumber).append(": ").append(itineraryContent);
                            }
                        }
                    }
                }
            }

            return pageContentBuilder.toString();

        } catch (Exception e) {
            log.error("提取页面内容失败: {}", e.getMessage(), e);
            return "";
        }
    }

    /**
     * 提取页面div内容
     * @param htmlContent HTML内容
     * @return 格式化的页面内容，如："Day 1: 熊猫基地、宽窄巷子；Day 2: 锦里、杜甫草堂"
     */
    private String extractPageDiv(String htmlContent) {
        try {
            Document doc = Jsoup.parse(htmlContent);
            // 查找行程表格
            Elements box = doc.select("div.print_table_box");

            return box.html();

        } catch (Exception e) {
            log.error("提取页面内容失败: {}", e.getMessage(), e);
            return "";
        }
    }

    /**
     * 从行程单元格中提取关键内容
     * @param cell 表格单元格
     * @return 提取的关键内容
     */
    private String extractItineraryContent(Element cell) {
        try {
            String cellText = cell.text().trim();

            // 移除一些不需要的内容
            cellText = cellText.replaceAll("【注：[^；。\n]*", ""); // 移除注释
            cellText = cellText.replaceAll("温馨提示[^；。\n]*", ""); // 移除温馨提示

            return cellText;

        } catch (Exception e) {
            log.error("提取行程内容失败: {}", e.getMessage(), e);
            return "";
        }
    }

    private Map<String, Object> buildRequestBody(String keyword, int pageIndex, int pageSize, int days, String departureCity, Boolean flag) {

        ResultVO<ProvinceCapital> sceneCityIdByName = systemClient.getSceneCityIdByName(departureCity);
        ProvinceCapital data = sceneCityIdByName.getData();

        Long departureCityId = data.getCtripCityId();
        if (departureCityId == null || departureCityId <= 0) {
            departureCityId = 28L; // 默认成都
        }

        Map<String, Object> requestBody = new HashMap<>();
        requestBody.put("contentType", "json");
        
        // head
        Map<String, Object> head = new HashMap<>();
        head.put("cid", "09031090210194607114");
        head.put("ctok", "");
        head.put("cver", "1.0");
        head.put("lang", "01");
        head.put("sid", "8888");
        head.put("syscode", "09");
        head.put("auth", "");
        head.put("extension", new Object[]{});
        requestBody.put("head", head);
        
        // client
        Map<String, Object> client = new HashMap<>();
        client.put("version", "");
        client.put("channel", 114);
        client.put("locale", "zh-CN");
        client.put("currency", "CNY");
        client.put("source", "NVacationSearchV2");
        client.put("cid", "1743501440388.a3d4ozuhoWXu");
        client.put("extras", new HashMap<>());

        Map<String, Object> location = new HashMap<>();
        location.put("lat", "");
        location.put("lon", "");
        location.put("cityId", departureCityId);
        location.put("cityType", 3);
        location.put("locatedCityId", 0);
        client.put("location", location);

        requestBody.put("client", client);
        
        // marketingInfo
        Map<String, Object> marketingInfo = new HashMap<>();
        marketingInfo.put("allianceId", 0);
        marketingInfo.put("sid", 0);
        requestBody.put("marketingInfo", marketingInfo);
        
        // destination
        Map<String, Object> destination = new HashMap<>();
        destination.put("poid", 0);
        destination.put("type", "");
        destination.put("keyword", keyword);
        requestBody.put("destination", destination);
        
        // filtered
        // 构建 DEPARTURE_CITY 的内层 item
        Map<String, Object> departureItem = new HashMap<>();
        departureItem.put("type", "DEPARTURE_CITY");
        departureItem.put("method", "FILTERED");

        if (departureCity != null && !departureCity.isEmpty()) {
            ResultVO<ProvinceCapital> sceneCityIdByName1 = systemClient.getSceneCityIdByName(departureCity);
            ProvinceCapital data1 = sceneCityIdByName1.getData();
            departureItem.put("value", "" + data1.getCtripCityId());
        } else {
            departureItem.put("value", "28");
        }

        Map<String, Object> departure = new HashMap<>();
        departure.put("type", "DEPARTURE_CITY");
        departure.put("method", "OR");
        departure.put("items", List.of(departureItem));

        // 构建 TRAVEL_DAYS 的内层 items
        List<Map<String, Object>> travelDayItems = new ArrayList<>();
        if (flag){
            for (int i = 1; i <= days; i++) {
                Map<String, Object> day = new HashMap<>();
                day.put("type", "TRAVEL_DAYS");
                day.put("method", "FILTERED");
                day.put("value", String.valueOf(i));
                travelDayItems.add(day);
            }
        } else {
            Map<String, Object> day = new HashMap<>();
            day.put("type", "TRAVEL_DAYS");
            day.put("method", "FILTERED");
            day.put("value", String.valueOf(days));
            travelDayItems.add(day);
        }

        Map<String, Object> travelDays = new HashMap<>();
        travelDays.put("type", "TRAVEL_DAYS");
        travelDays.put("method", "OR");
        travelDays.put("items", travelDayItems);

        // 加入到 filtered 的 items 中
        List<Map<String, Object>> items = new ArrayList<>();
        items.add(departure);
        items.add(travelDays);

        // ✅ 最终 filtered 构建
        Map<String, Object> filtered = new HashMap<>();
        filtered.put("tab", "126");
        filtered.put("preItems", new ArrayList<>());
        filtered.put("items", items); // ✅ 设置 items
        filtered.put("minPrice", 0);
        filtered.put("maxPrice", 0);
        filtered.put("beginDate", "");
        filtered.put("endDate", "");
        filtered.put("sort", 2);
        filtered.put("pageIndex", pageIndex);
        filtered.put("pageSize", pageSize);
        filtered.put("extras", new HashMap<>());

        requestBody.put("filtered", filtered);
        
        // searchOption
        Map<String, Object> searchOption = new HashMap<>();
        searchOption.put("returnMode", "all");
        searchOption.put("filters", new Object[]{});
        searchOption.put("needAdProduct", false);
        searchOption.put("needUpStream", false);
        searchOption.put("needRiskPolicyInfo", false);
        requestBody.put("searchOption", searchOption);
        
        // imageOption
        Map<String, Object> imageOption = new HashMap<>();
        imageOption.put("width", 420);
        imageOption.put("height", 420);
        imageOption.put("autoCrop", true);
        requestBody.put("imageOption", imageOption);
        
        // productOption
        Map<String, Object> productOption = new HashMap<>();
        productOption.put("needBasicInfo", true);
        productOption.put("needPrice", true);
        productOption.put("needVendor", false);
        productOption.put("needComment", false);
        productOption.put("needOrder", false);
        productOption.put("needRanking", false);
        requestBody.put("productOption", productOption);
        
        requestBody.put("productKeys", new Object[]{});
        requestBody.put("requestSource", "tour");
        requestBody.put("debug", false);
        
        // extras
        Map<String, Object> extras = getStringObjectMap();
        requestBody.put("extras", extras);

        return requestBody;
    }

    private static Map<String, Object> getStringObjectMap() {
        Map<String, Object> extras = new HashMap<>();
        extras.put("USE_OLD_RISK", "false");
        extras.put("USE_NEW_LEVEL", "false");
        extras.put("USE_NEW_PRICE", "true");
        extras.put("needUserDiscountPrice", "false");
        extras.put("NEED_DEPARTURE_TAB_REDIRECT", "false");
        extras.put("NEED_ALL_FILTERS", "false");
        extras.put("NEED_MIX_FILTER", "false");
        extras.put("FROM_PC_LIST", "false");
        extras.put("NEED_PRE_SALE_FILTER", "false");
        extras.put("USER_PAGE_FROM", "listpage");
        extras.put("DEFAULT_NATIVE_REQUEST", "false");
        extras.put("CONTAINER_REQUEST_FLAG", "false");
        extras.put("TAB_SCENE_CODE", "pc_online");
        extras.put("NEED_CPC_ADVERTISEMENT", "false");
        extras.put("ENABLE_PC_VACATION_SEARCH", "false");
        return extras;
    }

    // 只解析基本信息的方法
    private List<Product> parseAndSaveProducts(String responseBody) {
        try {
            JSONObject jsonResponse = JSON.parseObject(responseBody);
            JSONArray products = jsonResponse.getJSONArray("products");

            if (products == null || products.isEmpty()) {
                throw new RuntimeException("没有商品数据");
            }

            List<Product> productList = new ArrayList<>();

            for (int i = 0; i < products.size(); i++) {
                JSONObject productJson = products.getJSONObject(i);
                
                // 过滤掉DT类型
                String productType = productJson.getString("productType");
                if ("DT".equals(productType)) {
                    continue;
                }
                
                String productId = productJson.getString("id");

                // 检查是否已存在，存在则更新，不存在则新增
                Product existingProduct = getById(productId);
                Product product = existingProduct != null ? existingProduct : new Product();

                // 填充数据
                populateProductData(product, productJson);

                product.setId(productId);
                product.setProductType(productType);

                // 如果是新商品，设置初始状态
                if (existingProduct == null) {
                    product.setDetailFetched(false);
                    product.setCreateTime(LocalDateTime.now());
                }

                product.setUpdateTime(LocalDateTime.now());

                saveOrUpdate(product);

                log.info("{}商品: {}", existingProduct == null ? "新增" : "更新", productId);

                productList.add(product);
            }

            return productList;

        } catch (Exception e) {
            log.error("解析商品数据失败: {}", e.getMessage(), e);
            throw new RuntimeException(e.getMessage());
        }
    }

    public static Set<String> extractAndDeduplicateTags(String subtitle, String level1) {
        Set<String> result = new HashSet<>();
        List<String> rawNames = new ArrayList<>();

        // 按常见分隔符拆分
        if (subtitle != null) {
            rawNames.addAll(Arrays.asList(subtitle.split("[·+、，,]")));
        }
        if (level1 != null) {
            rawNames.addAll(Arrays.asList(level1.split("[·+、，,]")));
        }

        // 去前后空格 + 模糊去重
        for (String name : rawNames) {
            name = name.trim();
            if (name.isEmpty()) continue;

            boolean isDuplicate = false;
            for (String existing : result) {
                if (isSimilar(name, existing)) {
                    isDuplicate = true;
                    break;
                }
            }
            if (!isDuplicate) {
                result.add(name);
            }
        }

        return result;
    }

    // 简单模糊匹配：是否包含、是否前缀一致（你也可以引入更复杂的算法，如 Levenshtein）
    private static boolean isSimilar(String a, String b) {
        return a.contains(b) || b.contains(a) || a.startsWith(b) || b.startsWith(a);
    }

    private String processHtml(String htmlContent) {
        try {
            Document doc = Jsoup.parse(htmlContent);
            
            // 移除所有script标签
            Elements scripts = doc.select("script");
            scripts.remove();
            
            // 移除所有link标签（CSS引用）
            Elements links = doc.select("link");
            links.remove();

            cleanUnwantedElements(doc);

            // 在head中添加本地CSS引用
            Element head = doc.head();
            head.appendElement("link").attr("rel", "stylesheet").attr("href", "/css/common.css");
            head.appendElement("link").attr("rel", "stylesheet").attr("href", "/css/product.css");
            head.appendElement("link").attr("rel", "stylesheet").attr("href", "/css/detail.css");

            return doc.html();
            
        } catch (Exception e) {
            log.error("处理HTML失败: {}", e.getMessage(), e);
            return htmlContent;
        }
    }

    private void cleanUnwantedElements(Document doc) {
        // 移除打印页眉
        Elements printHeaders = doc.select("div.print_header_bg");
        printHeaders.remove();

        // 移除携程Logo
        doc.select("img[src*='c_logo2020.png']").remove();
        doc.select("a[href='/'] img.print_header_img").remove();

        // 移除包含Logo的父级元素
        Elements logoContainers = doc.select("div.print_header_box");
        logoContainers.remove();

        // 移除打印按钮（通过文本内容匹配）
        Elements spans = doc.select("span");
        spans.removeIf(span -> {
            String text = span.text().trim();
            return "打印纯文字版".equals(text) ||
                    "打印完整版（含图片）".equals(text) ||
                    span.hasClass("bg-color-0086f6");
        });

        // 移除具有特定样式的打印按钮（通过class匹配）
        Elements styledButtons = doc.select("span.absolute.right-0");
        for (Element button : styledButtons) {
            String text = button.text();
            if (text.contains("打印") || button.hasClass("bg-color-0086f6")) {
                button.remove();
            }
        }

        // 移除行程时间提示
        doc.select("p.style__g19w3l1").remove();

        // 移除编号和URL信息
        doc.select("p.print_title_tip").remove();

        // 通过文本内容移除包含特定信息的段落
        Elements paragraphs = doc.select("p");
        paragraphs.removeIf(p -> {
            String text = p.text().trim();
            return text.contains("编号：") ||
                    text.contains("https://vacations.ctrip.com/travel/detail/") ||
                    text.matches(".*行程时间均为当地时间.*");
        });

        // 移除空的div元素
        Elements emptyDivs = doc.select("div:empty");
        emptyDivs.remove();
    }

    private String saveStaticPage(String htmlContent, String fileName) throws IOException {
        // 确保目录存在
        File dir = new File(staticPagePath);
        if (!dir.exists()) {
            boolean mkdirs = dir.mkdirs();
        }
        
        String filePath = staticPagePath + File.separator + fileName;
        
        try (FileWriter writer = new FileWriter(filePath, false)) {
            writer.write(htmlContent);
        }
        
        return filePath;
    }
}